package com.maimao.kill.rabbit;

import com.alibaba.fastjson.JSON;
import com.baidu.fsg.uid.impl.CachedUidGenerator;
import com.google.common.collect.Lists;
import com.maimao.core.constant.KillConstant;
import com.maimao.core.model.resp.Result;
import com.maimao.core.utils.DateUtils;
import com.maimao.kill.client.AddressClient;
import com.maimao.kill.client.OrderClient;
import com.maimao.kill.config.RabbitProperties;
import com.maimao.kill.service.KillGoodsService;
import com.maimao.kill.service.KillService;
import com.maimao.model.info.Address;
import com.maimao.model.kill.KillGoods;
import com.maimao.model.kill.dto.KillDto;
import com.maimao.model.order.Order;
import com.maimao.model.order.OrderGoods;
import com.maimao.model.order.OrderShop;
import com.maimao.model.order.enums.OrderShopStatus;
import com.maimao.model.order.enums.OrderStatus;
import com.maimao.model.order.enums.OrderType;
import com.maimao.model.order.enums.ShipStatus;
import com.maimao.model.order.input.CommitOrder;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.maimao.core.constant.Constants.*;

/**
 * @author MaoLin Wang
 * @date 2021/4/17 10:42 上午
 */
@Slf4j
@Component
public class KillListener {

    private final CachedUidGenerator cachedUidGenerator;

    private final AddressClient addressClient;

    private final KillService killService;

    private final KillGoodsService killGoodsService;

    private final RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private OrderClient orderClient;


    public KillListener(CachedUidGenerator cachedUidGenerator, AddressClient addressClient, KillService killService, KillGoodsService killGoodsService, RedisTemplate redisTemplate) {
        this.cachedUidGenerator = cachedUidGenerator;
        this.addressClient = addressClient;
        this.killService = killService;
        this.killGoodsService = killGoodsService;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 接收秒杀成功的消息
     * <p>@RabbitHandler 接收对象类型消息</p>
     */
    @RabbitHandler
    @RabbitListener(queues = {RabbitProperties.KILL_SUCCESS_QUEUE}, containerFactory = "multiListenerContainer")
    public void processKillSuccess(Message message, Channel channel) throws Exception {

        KillDto dto = null;
        try {
            String msg = new String(message.getBody());
            log.info("receive kill success msg >>>> {}" + msg);
            dto = JSON.parseObject(msg, KillDto.class);
            CommitOrder commitOrder = generateOrder(dto);
            if (commitOrder == null) {
                //秒杀失败
                killService.killFail(dto.getKillId(), dto.getUserId());
            } else {
                //保存订单到redis
                saveOrder2Redis(commitOrder);
                //发送生成订单的请求
                rabbitService.sendSubmitOrderMessage(dto.setOrderId(commitOrder.getOrder().getId()));
            }
            //手动确认
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            log.error(e.getMessage());
            //直接忽略该秒杀成功消息
            if (dto != null) {
                killService.killFail(dto.getKillId(), dto.getUserId());
            }
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            throw e;
        }

    }

    /**
     * 接收秒杀成功预生成的订单信息
     * <p>@RabbitHandler 接收对象类型消息</p>
     */
    @RabbitHandler
    @RabbitListener(queues = {RabbitProperties.KILL_ORDER_SUBMIT_QUEUE}, containerFactory = "multiListenerContainer")
    public void processSubmitOrder(Message message, Channel channel) throws Exception {

        KillDto dto = null;
        try {
            String msg = new String(message.getBody());
            dto = JSON.parseObject(msg, KillDto.class);
            log.info("receive kill order msg >>>> {}" + dto);
            orderClient.killOrder(dto);
            rabbitService.sendKillOrderDelayMessage(dto);
            //手动确认
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            log.error(e.getMessage());
            if (dto != null) {
                killService.killFail(dto.getKillId(), dto.getUserId());
            }
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            throw e;
        }

    }

    /**
     * 保存预生成订单到reids
     *
     * @param commitOrder 订单
     */
    private void saveOrder2Redis(CommitOrder commitOrder) {
        redisTemplate.opsForValue().set(KillConstant.KILL_SUCCESS_ORDER + commitOrder.getOrder().getId(), commitOrder, RabbitProperties.PAY_EXPIRE_L, TimeUnit.MILLISECONDS);
    }

    /**
     * 生成订单信息
     *
     * @param dto 秒杀信息
     * @return 订单
     */
    private CommitOrder generateOrder(KillDto dto) {
        CommitOrder commitOrder = new CommitOrder();
        LocalDateTime now = DateUtils.now();
        Result<Address> defaultAddressRes = addressClient.defaultAddress(dto.getUserId());
        if (defaultAddressRes == null || defaultAddressRes.getData() == null) {
            log.error("秒杀失败，收货地址为空...");
            return null;
        }
        Address address = defaultAddressRes.getData();
        Order order = new Order()
                .setId(cachedUidGenerator.getUID())
                .setUserId(dto.getUserId())
                .setConsignee(address.getName())
                .setMobile(address.getMobile())
                .setAddress(address.getProvince() + BLANK + address.getCity() + BLANK + address.getArea() + BLANK + address.getAddress())
                .setUseIntegral(false)
                .setStatus(OrderStatus.NOT_PAY.getStatus())
                .setType(OrderType.SEC_KILL.getStatus())
                .setCreateTime(now)
                .setDeleted(false);
        commitOrder.setOrder(order);

        List<OrderShop> orderShopList = Lists.newArrayListWithCapacity(1);

        KillGoods details = killGoodsService.details(dto.getKillId());
        if (details == null) {
            log.error("秒杀失败，商品不存在");
            return null;
        }
        //秒杀商品不允许使用任何优惠(包括优惠券、积分等)
        OrderShop orderShop = new OrderShop()
                .setOrderId(order.getId())
                .setShopId(details.getShopId())
                .setDeliveryFee(BigDecimal.ZERO)
                .setCouponPrice(BigDecimal.ZERO)
                .setTotalPrice(details.getKillPrice())
                .setActualPrice(details.getKillPrice())
                .setStatus(OrderShopStatus.NOT_PAY.getStatus())
                .setShipSn(String.valueOf(cachedUidGenerator.getUID()))
                .setShipName("顺丰")
                .setChangeStatus(ZERO)
                .setInvoice(false)
                .setDeleted(false);

        List<OrderGoods> orderGoodsList = Lists.newArrayList();
        OrderGoods orderGoods = new OrderGoods()
                .setOrderId(order.getId())
                .setComment(false)
                .setSkuId(details.getSkuId())
                .setGoodsId(details.getGoodsId())
                .setTitle(details.getSkuName())
                .setNum(ONE)
                .setPrice(details.getKillPrice())
                //brandId
                .setSpec(details.getSkuParam())
                .setSpecDisplayName(details.getSpecStr())
                .setShippedStatus(ShipStatus.NOT_SHIP.getStatus())
                .setReceive(false)
                .setPicture(details.getPic())
                .setIntegral(details.getKillPrice().intValue() / 100);
        orderGoodsList.add(orderGoods);

        orderShop.setOrderGoodsList(orderGoodsList);
        orderShopList.add(orderShop);

        commitOrder.setOrderShopList(orderShopList);
        return commitOrder;


    }


}
